import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {MqttService} from 'ngx-mqtt';
import {combineLatest, forkJoin, merge, Observable, of, scheduled, Scheduler, Subject, Subscription, zip} from 'rxjs';
import {HttpClient} from '@angular/common/http';
import {environment} from '@environments';
import {iterator} from 'rxjs/internal-compatibility';
import {first, mergeAll, skipWhile} from 'rxjs/operators';
import * as net from 'net';

@Component({
  selector: 'app-self-check',
  templateUrl: './self-check.component.html',
  styleUrls: ['./self-check.component.scss']
})
export class SelfCheckComponent implements OnInit {
  @Input()
  deviceChangeObserver: Observable<any>;
  @Output()
  temperatureAvgDataChangeEvent = new EventEmitter();
  deviceInfo;
  topic = 'deviceStatus';
  subjectMap: any = {};
  chartInstanceMap: any = {};
  refreshSpeedModel = 5000;
  refreshSpeed = this.refreshSpeedModel;

  temperatureOptions: any = {
    chartId: 'temp',
    currentStatus: {
      cpuTemp: {value: 0},
      gpuTemp: {value: 0},
      diskTemp: {value: 0}
    },
    title: {
      text: ''
    },
    legend: {
      type: 'scroll',
      orient: 'vertical',
      left: 100,
      top: 60,
      data: ['cpuTemp', 'gpuTemp', 'diskTemp'],
      backgroundColor: '#f4f9ff'

    },
    tooltip: {
      trigger: 'axis',
      formatter: (params) => {
        params = params[0];
        return `${params.name}：${Math.ceil(params.value)}`;
      },
      axisPointer: {
        animation: false
      }
    },
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: [],
      axisLabel: {
        interval: (index: number, value: string) => {
          let labelValue = parseInt(value, 10);
          const speed = this.refreshSpeed / 1000;
          if (speed < 1) {
            labelValue *= speed;
          }
          const quotient = labelValue / 60;
          const remainder = labelValue % 60;
          return quotient > 0 && remainder === 0;
        },
        formatter: (value) => {
          return value * (this.refreshSpeed / 1000) / 60 + '分钟前';
        }
      }
    },
    yAxis: {
      type: 'value',
      min: 0,
      max: 100,
      boundaryGap: [0, '100%'],
      splitLine: {
        show: false
      }
    },
    series: [
      {name: 'cpuTemp', type: 'line', showSymbol: false, hoverAnimation: false, data: []},
      {name: 'gpuTemp', type: 'line', showSymbol: false, hoverAnimation: false, data: []},
      {name: 'diskTemp', type: 'line', showSymbol: false, hoverAnimation: false, data: []}
    ]
  };


  fanSpeedOptions: any = {
    chartId: 'fan',
    chartInitSubject: new Subject(),
    tooltip: {formatter: '{b} : {c}%'}, color: '#f49d80',
    series: [{
      name: '风扇转速', type: 'gauge', max: 6000, splitNumber: 6,
      progress: {show: true},
      title: {fontSize: 14},
      detail: {
        width: 40,
        height: 14,
        fontSize: 14,
        color: '#fff',
        backgroundColor: 'auto',
        borderRadius: 3,
        formatter: '{value}rpm'
      },
      data: [{
        value: 0, name: '风扇转速',
        title: {offsetCenter: ['0%', '80%']},
        detail: {width: 80, offsetCenter: ['0%', '99%']}
      }]
    }]
  };
  cpuOptions;
  memOptions;
  diskOptions;
  netOptions;
  gpuOptions;
  // 性能监控图配置项
  performanceOptionsList = [];
  private interval: NodeJS.Timeout;

  constructor(private mqttService: MqttService, private http: HttpClient) {
  }


  ngOnInit(): void {

    // this.performanceOptionsList.push(this.makeAreaOptions('cpu', 'CPU', '14% 2.8GHz', '#5470c6'));
    // this.performanceOptionsList.push(this.makeAreaOptions('mem', '内存', '5.4/7.9 GB(68%)', '#9a60b4'));
    // this.performanceOptionsList.push(this.makeAreaOptions('disc', '磁盘 0 (C:)', '8%', '#91cc75'));
    // this.performanceOptionsList.push(this.makeAreaOptions('net', '以太网', 'vEthernet (Default Switch)<br>发送：0 接收：0 Kbps', '#964c17'));
    // this.performanceOptionsList.push(this.makeAreaOptions('gpu', 'GPU 0', 'Intel(R) UHD Graphics 629<br>1%', '#2175b1'));
    this.cpuOptions = this.makeAreaOptions('cpu', 'CPU', '', '#5470c6');
    this.performanceOptionsList.push(this.cpuOptions);
    this.memOptions = this.makeAreaOptions('mem', '内存', '', '#9a60b4');
    this.performanceOptionsList.push(this.memOptions);
    this.diskOptions = this.makeAreaOptions('disk', '磁盘', '', '#91cc75');
    this.performanceOptionsList.push(this.diskOptions);
    this.netOptions = this.makeAreaOptions('net', '以太网', '', '#964c17');
    this.performanceOptionsList.push(this.netOptions);
    this.gpuOptions = this.makeAreaOptions('gpu', 'GPU', '', '#2175b1');
    this.performanceOptionsList.push(this.gpuOptions);


    this.deviceChangeObserver.subscribe(deviceInfo => {
      console.log('切换被测设备');
      this.deviceInfo = deviceInfo;
      // 初始化所有图表，准备展示新设备的状态数据
      this.initCharts();

    });
    // 初始化所有subject
    this.subjectMap[this.temperatureOptions.chartId] = new Subject();
    this.subjectMap[this.fanSpeedOptions.chartId] = new Subject();
    this.performanceOptionsList.forEach((item, index) => this.subjectMap[item.chartId] = new Subject());

    this.waitForChartInit();
  }

  waitForChartInit(): void {
    // combineLatest(Object.values(this.subjectMap))
    // FIXME: 等待所有表格初始化完成才开始获取下位机状态数据，这里实际使用forkJoin更合适，那样的话需要使用complete 替换next，或者说在现有next后面再加一句complete。
    zip(...Object.keys(this.subjectMap).map(k => this.subjectMap[k]))
      // .pipe(first())
      .subscribe((result) => {
        console.log('所有表格初始化完成', result);
        this.startGetStatusData(this.refreshSpeedModel);
      });
  }

  refreshSpeedChangeEvent(): void {
    this.refreshSpeed = this.refreshSpeedModel;
    if (this.refreshSpeed === 0) {
      return;
    }
    this.startGetStatusData(this.refreshSpeed);
  }

  startGetStatusData(ms): void {
    if (this.interval) {
      clearInterval(this.interval);
    }
    const showEmptyData = () => {
      console.log('填充空数据，确保时间线正常推进。');
      const now = new Date();
      const runningData = {
        cpuTemp: {date: now, value: 0},
        gpuTemp: {date: now, value: 0},
        diskTemp: {date: now, value: 0}
      };
      this.receiveTemperatureData(runningData);

      // 更新CPU占用图
      this.receiveSimpleValue(this.cpuOptions, 0, 0 + '% ' + this.deviceInfo.cpu[0].name);
      // 更新内存占用图
      this.receiveSimpleValue(this.memOptions, 0, 0 + '% ');
      // 更新磁盘占用图
      this.receiveSimpleValue(this.diskOptions, 0, 0 + '% ' + this.deviceInfo.disk[0].name);
    };
    this.interval = setInterval(() => {
      if (!this.deviceInfo) {
        showEmptyData();
        return;
      }
      this.http.post(environment.testAgentServerUrl.replace('${ip}', this.deviceInfo.ip) + 'req/pc/get_workstatus', {}).subscribe((status: any) => {
        // console.log(status);
        // 更新温度监控图
        const cpuTemp = (status.cpu_temperature as Array<any>).reduce((previousValue, item) => previousValue + this.getTemperature(item.curTemp), 0) / 4;
        const now = new Date();
        const runningData = {
          cpuTemp: {date: now, value: cpuTemp},
          gpuTemp: {date: now, value: this.getTemperature(status.gpu_temperature)},
          diskTemp: {date: now, value: status.disk_temperature}
        };
        this.receiveTemperatureData(runningData);
        // 更新CPU占用图
        this.receiveSimpleValue(this.cpuOptions, status.cpu_utilization, status.cpu_utilization + '% ' + this.deviceInfo.cpu[0].name);
        // 更新内存占用图
        this.receiveSimpleValue(this.memOptions, status.mem_utilization, status.mem_utilization + '% ');
        // 磁盘占用图
        this.receiveSimpleValue(this.diskOptions, 0, 0 + '% ' + this.deviceInfo.disk[0].name);
      }, showEmptyData);
    }, ms);
  }

  initCharts(): void {
    // 初始化温度监控组件数据
    this.initData(this.temperatureOptions);
    this.performanceOptionsList.forEach(item => {
      this.initData(item);
    });
  }

  initData(options): void {
    // 横轴初始化，创建300个坐标
    const showSeconds = 300;
    options.xAxis.data = [...Array(showSeconds + 1).keys()].reverse();
    options.xAxis.data.pop();
    // 纵轴初始化，所有纵轴赋值为0
    options.series.forEach(item => {
      // 清空历史数据
      item.data = Array(showSeconds).fill(0);
    });
  }

  receiveTemperatureData(runningData: any): void {

    Object.values(runningData).forEach((item: any) => item.value = Math.ceil(item.value));
    this.temperatureOptions.currentStatus = runningData;
    // const series = [];

    // 更新数据
    for (const seriesItem of this.temperatureOptions.series) {
      // console.log('');
      const name = seriesItem.name;
      const dataItem = runningData[name];

      seriesItem.data.shift();
      seriesItem.data.push(dataItem.value);

      const valueArr: Array<number> = seriesItem.data;
      const meaningfulData = valueArr.filter(item => item !== 0);
      if (meaningfulData.length > 0) {
        dataItem.max = Math.ceil(Math.max(...meaningfulData));
        dataItem.min = Math.ceil(Math.min(...meaningfulData));
        dataItem.avg = Math.ceil(meaningfulData.reduce((previousValue, currentValue) => previousValue + currentValue, 0) / meaningfulData.length);
      } else {
        dataItem.max = 0;
        dataItem.min = 0;
        dataItem.avg = 0;
      }
      // series.push({data: Object.assign([], seriesItem.data)});

    }
    this.temperatureAvgDataChangeEvent.emit(this.temperatureOptions.currentStatus);
    // 更新图例
    this.temperatureOptions.legend.formatter = (name) => {
      return `${name}\t\t最高：${runningData[name].max}°C \t最低：${runningData[name].min}°C \t平均：${runningData[name].avg}°C`;
    };

    this.chartInstanceMap[this.temperatureOptions.chartId].setOption(this.temperatureOptions);


  }

  receiveSimpleValue(options, value, desc): void {
    // 更新cpu图
    options.desc = desc;
    options.series[0].data.shift();
    options.series[0].data.push(value);
    this.chartInstanceMap[options.chartId].setOption(options);
  }

  onChartInit(chartName, e: any): void {
    this.chartInstanceMap[chartName] = e;
    this.subjectMap[chartName].next(chartName);
  }

  getTemperature(str): number {
    let temp = parseFloat(str.replace(/[°C ]/g, ''));
    if (isNaN(temp)) {
      temp = 0;
    }
    return temp;
  }

  someTooHot(): boolean {
    return this.tooHot('cpuTemp') || this.tooHot('gpuTemp') || this.tooHot('diskTemp');
  }

  tooHot(type: string): boolean {
    let maxTemp = 0;
    switch (type) {
      case 'cpuTemp':
        maxTemp = 80;
        break;
      case 'gpuTemp':
        maxTemp = 85;
        break;
      case 'diskTemp':
        maxTemp = 50;
        break;
    }
    return this.temperatureOptions.currentStatus[type].value > maxTemp;
  }

  makeGaugeOptions(name, color, max, splitNumber, detailFormatter, value): any {
    return {
      chartInitSubject: new Subject(),
      tooltip: {formatter: '{b} : {c}%'}, color,
      series: [{
        name, type: 'gauge', max, splitNumber,
        progress: {show: true},
        title: {fontSize: 14},
        detail: {
          width: 40,
          height: 14,
          fontSize: 14,
          color: '#fff',
          backgroundColor: 'auto',
          borderRadius: 3,
          formatter: detailFormatter
        },
        data: [{
          value, name,
          title: {offsetCenter: ['0%', '80%']},
          detail: {width: 80, offsetCenter: ['0%', '99%']}
        }]
      }]
    };
  }

  makeAreaOptions(chartId, name, desc, color): any {
    return {
      chartId,
      name,
      desc,
      color,
      xAxis: {
        show: false,
        type: 'category',
        boundaryGap: false,
        data: []
      },
      yAxis: {
        min: 0,
        max: 100,
        show: false,
        type: 'value',
        splitLine: {
          show: false
        }
      },
      grid: [
        {left: '0%', top: '0%', width: '100%', height: '100%'}
      ],
      series: [{
        data: [],
        type: 'line',
        areaStyle: {},
        smooth: true,
        showSymbol: false

      }]
    };
  }

}
